# -*-coding:utf-8-*-

from bson.objectid import ObjectId
from daos.base_async import BaseDao, BaseRecord
import pymongo
from tornado.gen import coroutine, Return
from utils.misc import limit_update_scope

'''
- PlatformDeviceHandler -
-------------------------
POST /platform/api/devices?access_token=ACCESS_TOKEN[&format=csv]

GET /platform/api/devices
Response: {

}

PUT /platform/api/devices/{did}
Request: {

}

状态：DID, 在线情况，余额，绑定者号码，位置，sim info(上线时更新)
控制：work mode(计费模式: 销售，租凭【按天，按流量】)

配置：计费模式、单价，出水量
'''
Devices_Document_Structure = {
    # 通用字段
    'device_id': 'DEVICE_ID',
    'module_vendor_id': 'MV_ID',  # 模块厂商
    'vendor_id': 'VENDOR_ID',  # 厂商
    'owner_id': 'OWNER_ID',
    'qc_id': 'QUALITY_CHECKER_ID',
    'product_id': 'PRODUCT_ID',

    'module_model': 'MODULE_MODEL',
    'module_batch_no': 'MODULE_BATCH_NO',

    'type': 'IPC | BELL | WPM',  # 通过 GET /platform/api/devices/types 接口获取平台支持的设备类型, product_type?
    'model': 'MODEL',
    'batch_no': 'BATCH_NO',

    'name': 'NAME',
    'location_name': 'LOCATION_NAME',

    'coordinates': [23.1, 76.5],
    'online_at': 1501152180,
    'offline_at': 1501252180,

    'enabled': True,
    'power_on': True,

    # specifics 字段隔离设备类型相关字段
    'specifics': {
        'ver': '0.1.2',
        'imsi': '460040889504426',
        'lbs': [0.0, 0.0],
        'iccid': '898602B8191651468766',
        'imei': '862991528671420',
        'build': '20170801',

        'rent': {
            'expire_time': 1501152180,  # INT, - 到期时间，自1970年1月1日00点00分00秒以来的秒数
            # -> 'days_remaining': INT, - 剩余天数, 该字段使用 expire_time 计算
            'total_used_time': 1501152180,  # INT - 累计使用时间，自1970年1月1日00点00分00秒以来的秒数
            'first_pay_time': 1501152180,
            'last_pay_time': 1501152180,
        },

        'wpm_quota': {
            'mode': 0,  # 机器工作模式

            # 按时间的租赁模式
            'expire_time': 12,
            # 'total_used_time': 12,
            # 'first_pay_time': 12,
            # 'last_pay_time': 12,

            # 按用量的租赁模式
            'remain': 12,
            'used': 13,
        },
    },

    'city': 'Zhuhai',
    'district': 'Xiangzhou',
}


class Device(BaseRecord):
    def __getattr__(self, item):
        return self._get_optional_field(item)


class DevicesDao(BaseDao):
    def __init__(self, db):
        super(DevicesDao, self).__init__(db, 'devices')
        self.user_fields = ['owner_id', 'name', 'coordinates', 'location_name', 'power_on', 'city', 'district']
        self.vendor_fields = ['model', 'batch_no', 'type', 'qc_id', 'product_id', 'enabled']
        self.module_vendor_fields = ['module_model', 'module_batch_no', 'vendor_id']
        self.required_fields = self.module_vendor_fields
        self.optional_fields = self.vendor_fields + self.user_fields
        self.readonly_fields = []

    def _get_record_object(self, data):
        return Device(data)

    @coroutine
    def is_device_exists(self, device_id):
        result = yield self.count({'device_id': device_id})
        raise Return(1 == result)

    @coroutine
    def get_owner_of_device(self, device_id):
        result = yield self.find_one({'device_id': device_id}, {'_id': 0, 'owner_id': 1})
        if 'owner_id' in result:
            raise Return(result['owner_id'])
        else:
            raise Return(None)

    @coroutine
    def get_device(self, uid, device_id):
        filter_cond = {'device_id': device_id, 'owner_id': uid}
        projection = self.user_fields + ['product_id', 'online_at', 'offline_at', 'specifics']
        device = yield self.find_one(filter_cond, projection)
        del device['_id']
        del device['owner_id']
        raise Return(device)

    @coroutine
    def get_device_specifics(self, device_id, field):
        filter_cond = {'device_id': device_id}
        result = yield self.find_one(filter_cond, {'_id': 0, 'specifics.%s' % field: 1})
        if result is not None and 'specifics' in result and field in result['specifics']:
            raise Return(result['specifics'][field])
        else:
            raise Return({})

    @coroutine
    def platform_get_device(self, uid, device_id):
        device = yield self.find_one({'device_id': device_id, 'vendor_id': uid}, {'_id': 0})
        raise Return(device)

    @coroutine
    def update_device(self, uid, device_id, data):
        filter_cond = {'device_id': device_id}
        fields = self.extract_fields(data, optional_fields=self.user_fields)
        if 0 == len(fields):
            raise Return(False)
        modification = {'$set': fields}
        result = yield self.update_one(filter_cond, modification)
        raise Return(1 == result.matched_count)

    @coroutine
    def delete_device(self, uid, device_id):
        filter_cond = {'device_id': device_id, 'owner_id': uid}
        update = {'$unset': dict([(x, 0) for x in self.user_fields])}
        result = yield self.update_one(filter_cond, update)
        raise Return(1 == result.matched_count)

    # TODO: 限制导入设备导入数量: 'vendor_id': uid,
    @coroutine
    def platform_batch_create_device(self, uid, device_ids, data):
        device_ids = list(set(device_ids))
        device_nr = len(device_ids)
        filter_cond = {'device_id': {'$in': device_ids}}
        count = yield self.count(filter_cond)
        if 0 < count:
            raise Exception('some device_id[s] already exist')
        modification = self.extract_fields(data, required_fields=self.required_fields)
        modification['module_vendor_id'] = uid
        devices = []
        for device_id in device_ids:
            device = {'device_id': device_id}
            device = dict(device, **modification)
            devices.append(device)
        result = yield self.insert_many(devices)
        raise Return(len(result.inserted_ids) == device_nr)

    @coroutine
    def platform_batch_update_device(self, uid, device_ids, data):
        device_ids = list(set(device_ids))
        device_nr = len(device_ids)
        filter_cond = {'vendor_id': uid, 'device_id': {'$in': device_ids}}
        count = yield self.count(filter_cond)
        if device_nr != count:
            raise Exception('device_id[s] not exist')
        modification = self.extract_fields(data, optional_fields=self.optional_fields)
        if 0 < len(modification):
            result = yield self.update_many(filter_cond, {'$set': modification})
            raise Return(device_nr == result.matched_count)
        else:
            raise Return(False)

    @coroutine
    def platform_batch_update_device_specifics(self, uid, device_ids, data):
        device_nr = len(device_ids)
        filter_cond = {'vendor_id': uid, 'device_id': {'$in': device_ids}}
        count = yield self.count(filter_cond)
        if device_nr != count:
            raise Exception('device_id[s] not exist')
        update = limit_update_scope({'$set': data['specifics']})
        result = yield self.update_many(filter_cond, update)
        raise Return(device_nr == result.matched_count)

    @coroutine
    def platform_batch_delete_device(self, uid, device_ids):
        device_ids = list(set(device_ids))
        device_nr = len(device_ids)
        # filter_cond = {'vendor_id': uid, 'device_id': {'$in': device_ids}}
        filter_cond = {'device_id': {'$in': device_ids}}
        count = yield self.count(filter_cond)
        if device_nr != count:
            raise Exception('device_id[s] not exist')
        result = yield self.delete_many(filter_cond)
        raise Return(device_nr == result.deleted_count)

    @coroutine
    def internal_update_device(self, device_id, data):
        filter_cond = {'device_id': device_id}
        result = yield self.update_one(filter_cond, {'$set': data})
        raise Return(1 == result.matched_count)

    @coroutine
    def internal_get_device(self, device_id, fields):
        filter_cond = {'device_id': device_id}
        result = yield self.find_one(filter_cond, fields)
        del result['_id']
        raise Return(result)

    @coroutine
    def internal_update_device_specifics(self, device_id, field, data):
        filter_cond = {'device_id': device_id}
        result = yield self.update_one(filter_cond, {'$set': {'specifics.%s' % field: data}})
        raise Return(1 == result.matched_count)
